package com.scitc.controller;

import com.alibaba.fastjson.JSON;
import com.scitc.dto.matchDto.MatchFileDto;
import com.scitc.dto.matchDto.MatchOnceDto;
import com.scitc.model.HistoryItem;
import com.scitc.model.Result;
import com.scitc.model.User;
import com.scitc.service.HistoryService;
import com.scitc.service.MatchService;
import com.scitc.utils.IpUtil;
import com.scitc.utils.JwtUtil;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.File;
import java.io.InputStream;
import java.util.Date;
import java.util.Map;
import java.util.UUID;

/**
 * @author: 高进秋
 * @date 2021年6月25日22:02:56
 * <p>
 * 前端需要两个参数。
 * 参数1 status，表示执行的操作状态码， 1 ： 进行训练操作，name为要训练的文件名 2：单次匹配，name 为 要匹配的文本 3：批量匹配，name 为要匹配的csv名
 * status 1,3 需要将文件先上传至resources 下，Address/file/下，不然是找不到这个文件的
 * <p>
 * 例如：
 * status = 1 ,name =  标准地址结构化数据样例.csv  成功返回 成功字符串，失败返回失败 字符串
 * status = 2 ,name =  江苏南京市玄武区曹后村公交宿舍三幢  成功返回 解析成功后的Json字符串，失败返回 失败 字符串
 * status = 3 ,name =  非标准地址数据样例.csv  成功返回 成功字符串，失败返回失败 字符串
 * <p>
 * 例如：三个请求对应三个操作：单次匹配、上传文件批量匹配、上传文件训练模型
 */
@RestController
public class AddressMatchController {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private MatchService matchService;

    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;

    @Autowired
    private IpUtil ipUtil;

    @Autowired
    private HistoryService historyService;

    @PostMapping("/matchonce")
    public Object matchOnce(HttpServletRequest request,
                            @RequestBody MatchOnceDto matchOnceDto) {
        String token = matchOnceDto.getToken();
        String address = matchOnceDto.getAddress();

        if (token == null) {
            if (ipUtil.unLoginIpCheck(request, redisTemplate) == 0) {
                return Result.fail("使用次数不足，请登陆");
            }
        } else {
            switch (jwtUtil.verify(token)) {
                case 2:
                    return Result.fail("令牌已经过期");
                case 0:
                    return Result.fail("令牌无效");
            }
        }

        Map<String, String> mp = (Map) JSON.parse(matchService.matchOnce(address));
        String all = "";
        for (String key : mp.keySet()) {
            mp.put(key, mp.get(key).replaceAll("None", ""));
            all += mp.get(key);
        }
        mp.put("all", all);

        // 如果登录，保存历史
        if (token != null) {
            String id = (String) jwtUtil.analysis(token).get("id");
            HistoryItem historyItem = new HistoryItem(
                    UUID.randomUUID().toString(),
                    id,
                    false,
                    address,
                    all,
                    new Date());
            // 如果超过最大条数20条，则删除最早的那一条
            if(historyService.countHis(id) >= 20) {
                int removeFlag = historyService.removeHis(historyService.findTheOldest(id).getUid());
                if(removeFlag != 1) {
                    return Result.fail("系统错误"); // 删除历史失败
                }
            }
            historyService.addHis(historyItem);
        }

        //调用python接口, 并返回数据
        return Result.success(mp);
    }

    /*
    * 先上传一个.csv文件到resources/Address/file 下,随后将该文件名交给service层调用python进行处理
    * 例如
    * name = 非标准地址数据样例.csv  // 这个文件已经在file里了。
    * return:
    * {
        "rulest": "训练成功！"
       }
    * 并且file 下新增一个 sc非标准地址数据样例.csv
    * */
    @PostMapping("/matchfile")
    public Result matchFile(@RequestParam("file") MultipartFile file, @RequestParam("token") String token, HttpServletResponse response) {

        //检测是否登录
        if (token == null) {
            return Result.fail("登录后才能使用该功能");
        }

        // 验证token是否有效
        switch (jwtUtil.verify(token)) {
            case 2:
                return Result.fail("令牌已经过期");
            case 0:
                return Result.fail("令牌无效");
        }

        String fileName = file.getOriginalFilename();
        String path = System.getProperty("user.dir")
                + "\\src\\main\\resources\\Address\\file\\";

        // 上传文件
        try {
            File fileExist = new File(path);
            if (!fileExist.exists()) {
                fileExist.mkdirs();
            }

            File newFile = new File(path, fileName);
            file.transferTo(newFile);
        } catch (Exception e) {
            return Result.fail(e.toString());
        }

//         上传文件至 resources/Address/file
//         以下是调用python接口，上传完了之后把文件名给service，然后调用python进行解析处理
//         会返回一个json，如
//         {
//            "rulest": "批量操作成功！"  //这里可能是失败
//          }
//         提示训练成功就会在

        if (matchService.matchFile(fileName).equals("批量操作成功！")) {

            // 保存历史记录
            String id = jwtUtil.analysis(token).getId();
            HistoryItem historyItem = new HistoryItem(
                    UUID.randomUUID().toString(),
                    id,
                    true,
                    fileName,
                    "文档不支持查看",
                    new Date());
            // 如果超过最大条数20条，则删除最早的那一条
            if(historyService.countHis(id) >= 20) {
                int removeFlag = historyService.removeHis(historyService.findTheOldest(id).getUid());
                if(removeFlag != 1) {
                    return Result.fail("系统错误"); // 删除历史失败
                }
            }
            historyService.addHis(historyItem);


            // 提供文件下载
            InputStream inputStream = null;
            ServletOutputStream servletOutputStream = null;
            try {
                Resource resource = new DefaultResourceLoader().getResource("classpath:Address\\file\\" + fileName);

                response.setContentType("application/force-download");
                response.setCharacterEncoding("UTF-8");

                inputStream = resource.getInputStream();
                servletOutputStream = response.getOutputStream();
                IOUtils.copy(inputStream, servletOutputStream);
                response.flushBuffer();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (servletOutputStream != null) {
                        servletOutputStream.close();
                        servletOutputStream = null;
                    }
                    if (inputStream != null) {
                        inputStream.close();
                        inputStream = null;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return null;
//            return Result.success("操作成功", path + "sc" + fileName);
        } else {
            return Result.fail("操作失败");
        }
//        return Result.success(null);
    }

    /*
    * 与manyMatch类似，也是上传文件成功后，给service文件名。依然是上传至file下
    * 例如
    *  name = 标准地址结构化数据样例.csv
    *  return:
    * {
        "rulest": "训练成功！"
        }
    * 如果训练了新的结果，那么file下的 标准模型.json 会增加新的内容，这个不用管，成功了有反馈就行
    * */
    @PostMapping("/train")
    public Result train(@RequestParam("file") MultipartFile file, @RequestParam("token") String token, HttpServletResponse response) {

        // 检测是否登录
        if (token == null) {
            return Result.fail("登录后才能使用该功能");
        }

        // 验证token是否有效
        switch (jwtUtil.verify(token)) {
            case 2:
                return Result.fail("令牌已经过期");
            case 0:
                return Result.fail("令牌无效");
        }

        String fileName = file.getName();
        String path = System.getProperty("user.dir")
                + "\\src\\main\\resources\\Address\\file\\";

        // 上传文件
        try {
            File fileExist = new File(path);
            if (!fileExist.exists()) {
                fileExist.mkdirs();
            }

            File newFile = new File(path, fileName);
            file.transferTo(newFile);
        } catch (Exception e) {
            return Result.fail(e.toString());
        }

        // 上传成功后
        if (matchService.train(fileName).equals("训练成功！")) {
            return Result.success("操作成功", null);
        } else {
            return Result.fail("文件格式错误");
        }
    }

}
